﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using Jayden.Dll.Core.Persistence;

namespace Jayden.Dll.Core.Membership
{
	public class RoleProvider : System.Web.Security.RoleProvider
	{
		private string m_ApplicationName = "Jayden";
		private Configuration m_Configuration;

		public override string ApplicationName { get { return m_ApplicationName; } set { m_ApplicationName = value; } }

		private Dictionary<string, Role> m_RoleNames = null;

		public Configuration Configuration
		{
			get
			{
				if (m_Configuration == null)
					m_Configuration = new Configuration(HostingEnvironment.MapPath("/Configuration.xml"));
				return m_Configuration;
			}
		}
		public MaybeDisposable<UnitOfWork> UnitOfWork
		{
			get
			{
				HttpContext context = HttpContext.Current;
				if (context != null)
				{
					UnitOfWork uow = context.Items["UnitOfWork"] as UnitOfWork;
					if (uow != null)
						return new MaybeDisposable<UnitOfWork>(uow, false);
				}
				return new MaybeDisposable<UnitOfWork>(new UnitOfWork(Configuration.GetConfiguration("UnitOfWork")));
			}
		}

		public override void CreateRole(string roleName)
		{
			lock (this)
			{
				using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
				{
					RoleDataAccess roleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<Role>() as RoleDataAccess;
					if (!RoleNames.ContainsKey(roleName))
					{
						Role role = new Role(uow.Disposable);
						role.RoleName = roleName;
						roleDataAccess.Create(role);
						m_RoleNames.Add(roleName, role);
					}
				}
			}
		}

		public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
		{
			lock (this)
			{
				using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
				{
					RoleDataAccess roleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<Role>() as RoleDataAccess;
					if (RoleNames.ContainsKey(roleName))
					{
						UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
						if (throwOnPopulatedRole)
						{
							List<UserRole> userRoles = userRoleDataAccess.FindByRoleName(roleName);
							if (userRoles != null && userRoles.Count > 0)
								throw new Exception(string.Format("The role '{0}' is populated with {1} users and cannot be deleted.", roleName, userRoles.Count));
						}
						roleDataAccess.DeleteByName(roleName);
						m_RoleNames.Remove(roleName);
						userRoleDataAccess.DeleteByRoleName(roleName);
						return true;
					}
					return false;
				}
			}
		}

		protected Dictionary<string, Role> RoleNames
		{
			get
			{
				if (m_RoleNames == null)
				{
					lock (this)
					{
						if (m_RoleNames == null)
						{
							using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
							{
								RoleDataAccess roleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<Role>() as RoleDataAccess;
								int totalRecords;
								List<Role> roles = roleDataAccess.FindAll(0, int.MaxValue, out totalRecords);
								Dictionary<string, Role> dico = new Dictionary<string, Role>();
								foreach (Role r in roles)
									dico.Add(r.RoleName, r);
								m_RoleNames = dico;
							}
						}
					}
				}
				return m_RoleNames;
			}
		}

		public override string[] GetAllRoles()
		{
			return RoleNames.Keys.ToArray();
		}

		public override bool RoleExists(string roleName)
		{
			return RoleNames.ContainsKey(roleName);
		}

		public override string[] GetRolesForUser(string username)
		{
			using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
			{
				UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
				List<UserRole> roles = userRoleDataAccess.FindByUserName(username);
				List<string> names = new List<string>();
				foreach (UserRole r in roles)
					names.Add(r.RoleName);
				return names.ToArray();
			}
		}

		public override string[] GetUsersInRole(string roleName)
		{
			using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
			{
				UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
				List<UserRole> roles = userRoleDataAccess.FindByRoleName(roleName);
				List<string> names = new List<string>();
				foreach (UserRole r in roles)
					names.Add(r.UserName);
				return names.ToArray();
			}
		}

		public override bool IsUserInRole(string username, string roleName)
		{
			using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
			{
				UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
				return userRoleDataAccess.Read(username, roleName) != null;
			}
		}

		public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
		{
			using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
			{
				UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
				foreach (string username in usernames)
					foreach (string rolename in roleNames)
						userRoleDataAccess.Delete(username, rolename);
			}
		}

		public override string[] FindUsersInRole(string roleName, string usernameToMatch)
		{
			using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
			{
				UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
				List<UserRole> roles = userRoleDataAccess.FindByRoleName(roleName, usernameToMatch);
				List<string> names = new List<string>();
				foreach (UserRole r in roles)
					names.Add(r.UserName);
				return names.ToArray();
			}
		}

		public override void AddUsersToRoles(string[] usernames, string[] roleNames)
		{
			using (MaybeDisposable<UnitOfWork> uow = UnitOfWork)
			{
				UserRoleDataAccess userRoleDataAccess = uow.Disposable.DataAccessManager.GetDataAccess<UserRole>() as UserRoleDataAccess;
				foreach (string username in usernames)
					foreach (string rolename in roleNames)
						userRoleDataAccess.Create(username, rolename);
			}
		}
	}
}
